package com.example.video.service;

import com.example.video.common.Constants;
import com.example.video.domain.Device;
import com.example.video.domain.DeviceInformation;
import com.example.video.domain.Information;
import com.example.video.repository.DeviceInformationRepository;
import com.example.video.repository.DeviceRepository;
import com.example.video.repository.InformationRepository;
import com.example.video.service.inter.IInformationService;
import com.example.video.vo.NoticeVo;
import com.example.video.vo.UpdateVo;
import com.example.video.vo.VideoVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Service
public class InformationServiceImpl implements IInformationService {

    private final InformationRepository informationRepository;
    private final DeviceRepository deviceRepository;
    private final DeviceInformationRepository deviceInformationRepository;

    @Autowired
    public InformationServiceImpl(InformationRepository informationRepository, DeviceInformationRepository deviceInformationRepository, DeviceRepository deviceRepository) {
        this.informationRepository = informationRepository;
        this.deviceInformationRepository = deviceInformationRepository;
        this.deviceRepository = deviceRepository;
    }

    @Override
    public Long addOrUpdateInformation(Information information) {
        informationRepository.saveAndFlush(information);
        return information.getId();
    }

    @Override
    @Transactional
    public void publishInformation(Long informationId, List<String> deviceImeis) {
        informationRepository.findById(informationId).ifPresent(information -> {
            deviceInformationRepository.deleteByPk_Information_Id(informationId);
            List<DeviceInformation> deviceInformations =
                    deviceRepository.findByImeiIn(deviceImeis)
                            .stream()
                            .map(device -> new DeviceInformation(device, information))
                            .collect(Collectors.toList());
            deviceInformationRepository.saveAll(deviceInformations);
        });
    }

    @Override
    public List<Information> getInformationList(int page, int size) {
        return informationRepository.findAll(PageRequest.of(page, size)).getContent();
    }

    @Override
    @Transactional
    public void deleteInformations(List<Long> informationIds) {
        for (Long id : informationIds) {
            deviceInformationRepository.deleteByPk_Information_Id(id);
            informationRepository.deleteById(id);
        }
    }

    @Override
    public List<VideoVo> queryVideo(String imei, Long start, Long end) {
        List<DeviceInformation> deviceInformations;
        if (end != null) {
            deviceInformations = deviceInformationRepository.queryInformation(imei, new Date(start), new Date(end), Constants.InformationTypeEnum.VEDIO.getCode());
        } else {
            deviceInformations = deviceInformationRepository.queryInformationWithoutEndTime(imei, new Date(start), Constants.InformationTypeEnum.VEDIO.getCode());
        }
        return assembleVideoVoList(deviceInformations);
    }

    @Override
    public List<NoticeVo> queryNotice(String imei, Long start, Long end) {
        List<DeviceInformation> deviceInformations;
        if (end != null) {
            deviceInformations = deviceInformationRepository.queryInformation(imei, new Date(start), new Date(end), Constants.InformationTypeEnum.NOTICE.getCode());
        } else {
            deviceInformations = deviceInformationRepository.queryInformationWithoutEndTime(imei, new Date(start), Constants.InformationTypeEnum.NOTICE.getCode());
        }
        return assembleNoticeVoList(deviceInformations);
    }

    @Override
    public List<UpdateVo> queryUpdate(int page, int size) {
        return assembleUpdateVoList(informationRepository.queryUpdate(PageRequest.of(page, size)).getContent());
    }

    @Override
    public List<Device> informationPublishedDevices(Long informationId) {
        return deviceInformationRepository.findByPk_Information_Id(informationId).stream().map(df -> df.getPk().getDevice()).collect(Collectors.toList());
    }

    private List<NoticeVo> assembleNoticeVoList(List<DeviceInformation> deviceInformations) {
        return deviceInformations.stream().map(this::assembleNoticeVo).collect(Collectors.toList());
    }

    private NoticeVo assembleNoticeVo(DeviceInformation deviceInformation) {
        NoticeVo noticeVo = new NoticeVo();
        noticeVo.setId(deviceInformation.getPk().getInformation().getId());
        noticeVo.setPosition(deviceInformation.getPk().getInformation().getPosition());
        noticeVo.setPublishDate(deviceInformation.getPk().getInformation().getPublishDate().getTime());
        noticeVo.setRemark(deviceInformation.getPk().getInformation().getRemark());
        noticeVo.setTitle(deviceInformation.getPk().getInformation().getTitle());
        noticeVo.setContent(deviceInformation.getPk().getInformation().getContent());
        return noticeVo;
    }

    private List<VideoVo> assembleVideoVoList(List<DeviceInformation> deviceInformations) {
        return deviceInformations.stream().map(this::assembleVideoVo).collect(Collectors.toList());
    }

    private VideoVo assembleVideoVo(DeviceInformation deviceInformation) {
        VideoVo videoVo = new VideoVo();
        videoVo.setId(deviceInformation.getPk().getInformation().getId());
        videoVo.setPosition(deviceInformation.getPk().getInformation().getPosition());
        videoVo.setPublishDate(deviceInformation.getPk().getInformation().getPublishDate().getTime());
        videoVo.setRemark(deviceInformation.getPk().getInformation().getRemark());
        videoVo.setTitle(deviceInformation.getPk().getInformation().getTitle());
        videoVo.setUrl(deviceInformation.getPk().getInformation().getContent());
        return videoVo;
    }

    private List<UpdateVo> assembleUpdateVoList(List<Information> information) {
        return information.stream().map(this::assembleUpdateVo).collect(Collectors.toList());
    }

    private UpdateVo assembleUpdateVo(Information information) {
        if (information != null) {
            UpdateVo updateVo = new UpdateVo();
            updateVo.setId(information.getId());
            updateVo.setPublishDate(information.getPublishDate().getTime());
            updateVo.setRemark(information.getRemark());
            updateVo.setTitle(information.getTitle());
            updateVo.setUrl(information.getContent());
            return updateVo;
        } else {
            return null;
        }
    }

}
